/******************* ?Marvell Semiconductor, Inc., ***************************
 *
 *  Purpose:    This module provides implementaion of ISR, DPC and other 
 *              interrupt related routines
 *
 *  $Author: schiu $
 *
 *  $Date: 2004/10/27 $
 *
 *  $Revision: #3 $
 *
 *****************************************************************************/

/*
===============================================================================
INCLUDE FILES
===============================================================================
*/
#include "precomp.h"
#include "If.h"
#include "pkfuncs.h"
#include "txproc.h"
#include "intproc.h"
#include "wlan_thread.h"
#include "rxproc.h"

typedef struct _INT_PARAM
{
	IF_REGVAL				ucGHostIntStatus;
	CRITICAL_SECTION		IntCriticalSection;
	ULONG					SoftIntStatus;
	ULONG					WaitEventType;
	HANDLE					hControllerInterruptEvent;

	///	
	PMRVDRV_ADAPTER			pAdapter;
} INT_PARAM, *PINT_PARAM;

static INT_PARAM	IntParam;
static WCHAR*	GetIntrTypeStrn(IF_PKT_TYPE	IntrType);
static VOID HandleSoftInterrupt(PINT_PARAM	pIntParam);
static VOID HandleUpldInterrupt(PINT_PARAM	pIntParam, IF_REGVAL	ucLHostIntStatus);

///////////////////////////////////////////////////////////////////////////////
//  SDNdisInterruptCallback - interrupt handler
//  Input:  hDevice - handle to the SDIO device
//          pAdapter - adapter context
//  Output: 
//  Returns:  SD_API_STATUS code
//  Notes:  
//
//      SD_API_STATUS_SUCCESS  is returned for HW to acknowledge the interrupt
///////////////////////////////////////////////////////////////////////////////
SD_API_STATUS NdisInterruptCallback(PVOID hDevice, IN PVOID ipIntParam)
{   
	PINT_PARAM		pIntParam = (PINT_PARAM)ipIntParam;
   	IF_REGVAL	ucHostIntStatus;
	SD_API_STATUS    status;

	DBGPRINT(DBG_ISR|DBG_HELP, (L"Enter %s\r\n", TEXT(__FUNCTION__)));

    //clear Int work around timeout count
    pIntParam->pAdapter->IntWARTimeoutCount = 0;
        
	status = If_FetchHostIntr(pIntParam->pAdapter, &ucHostIntStatus);
	if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ISR|DBG_ERROR,(L"Unable to read Host interrupt status register\n"));
        return status;
    }

    if (ucHostIntStatus == 0) {
        DBGPRINT(DBG_ISR|DBG_ERROR, (L"ISR: Not our interrupt\r\n"));
        return SD_API_STATUS_SUCCESS;
    }
	EnterCriticalSection(&pIntParam->IntCriticalSection);   
    pIntParam->ucGHostIntStatus |= ucHostIntStatus; 
    LeaveCriticalSection(&pIntParam->IntCriticalSection);
	If_ResetIntr(pIntParam->pAdapter, ucHostIntStatus);
    SetEvent(pIntParam->hControllerInterruptEvent);
  
    return SD_API_STATUS_SUCCESS;
}



static WCHAR*	GetIntrTypeStrn(IF_PKT_TYPE	IntrType)
{
	switch (IntrType) {
		case IF_DATA_PKT:
			return L"IF_DATA_PKT";
		case IF_CMD_PKT:
			return L"IF_CMD_PKT";
		case IF_TX_DONE_EVENT:
			return L"IF_TX_DONE_EVENT";
		case IF_MAC_EVENT:
			return L"IF_MAC_EVENT";
		default:
			return L"INVALID_PKT_TYPE";
	}
}

static VOID HandleSoftInterrupt(PINT_PARAM	pIntParam)
{
    BOOLEAN     CmdDnldStatus;
    int         TmpDnldTicket;

	if ( pIntParam->SoftIntStatus )
	{
		//add internal software interrupt event 
		if(pIntParam->SoftIntStatus & MRVL_SOFT_INT_ChipReset)
		{
			DBGPRINT(DBG_OID|DBG_PS|DBG_HELP,(L"Post handle Chip Reset\r\n"));
			pIntParam->SoftIntStatus &= ~MRVL_SOFT_INT_ChipReset; 
			                                       
			ProcessOIDResetChip(pIntParam->pAdapter);
                                               
            //Application may reset chip in sleep state. so,
            //we need to turn on all flags to force driver leave ps state. 
            pIntParam->pAdapter->PSMode = Ndis802_11PowerModeCAM;  
            pIntParam->pAdapter->psState = PS_STATE_FULL_POWER;
            pIntParam->pAdapter->bPSConfirm=FALSE; 

            pIntParam->pAdapter->bNullFrameSent = 0;

            pIntParam->WaitEventType |= MRVL_SOFT_INT_ChipReset;
            Signal_Pending_OID(pIntParam->pAdapter);
        
            if(pIntParam->pAdapter->MediaConnectStatus == NdisMediaStateConnected)
                HandleMACEvent(pIntParam->pAdapter,MACREG_INT_CODE_LINK_LOSE_NO_SCAN);  
        }
		if(pIntParam->SoftIntStatus & MRVL_SOFT_INT_AutoDeepSleep)
		{                                        
            OID_MRVL_DS_DEEP_SLEEP ds;
 
            DBGPRINT(DBG_OID|DBG_PS|DBG_HELP,(L"Post handle Chip Reset\r\n"));
            pIntParam->SoftIntStatus &= ~MRVL_SOFT_INT_AutoDeepSleep; 
                                                             
            ds.ulEnterDeepSleep = 1;  //enter deep sleep
            ProcessOIDDeepSleep(pIntParam->pAdapter,0xff,sizeof(OID_MRVL_DS_DEEP_SLEEP),(PVOID)&ds,HostCmd_PENDING_ON_NONE);  //051407
            pIntParam->pAdapter->IsAutoDeepSleep = 0; //051407
		}
		
        if ( pIntParam->SoftIntStatus & MRVL_SOFT_INT_TxRequest )
        {
            SendOneQueuedPacket(pIntParam->pAdapter->pTxParam );
            pIntParam->SoftIntStatus &= ~((ULONG)MRVL_SOFT_INT_TxRequest);
        }
		if (pIntParam->SoftIntStatus & MRVL_SOFT_INT_CMD) {
            TmpDnldTicket = RequestDnldTicket(pIntParam->pAdapter->pDnLdParam, DNLDT_CMD, SetCmdSoftIntr, pIntParam->pAdapter);
			if (TmpDnldTicket > 0) {
                pIntParam->pAdapter->CmdDnLdTicket = TmpDnldTicket;
				CmdDnldStatus = GetCmdFromQueueToExecute(pIntParam->pAdapter);
                if (CmdDnldStatus == FALSE) {
                    ReleaseDnldTicket(pIntParam->pAdapter->pDnLdParam, pIntParam->pAdapter->CmdDnLdTicket);
                }
			}
			pIntParam->SoftIntStatus &= ~((ULONG)MRVL_SOFT_INT_CMD);
		}

    }

	return;

}

static VOID HandleUpldInterrupt(PINT_PARAM	pIntParam, IF_REGVAL	ucLHostIntStatus)
{
	UCHAR				ucCardStatus;
	UCHAR				macEvent;
	USHORT				usLength;
    static HostCmd_DS_CODE_DNLD    *p_pkt=NULL;
	PVOID                       pRxBufVM=NULL;

	DBGPRINT(DBG_ISR|DBG_HELP, (L"Enter %s, (%xh)\r\n", TEXT(__FUNCTION__), ucLHostIntStatus));
	while (ucLHostIntStatus) {
		IF_API_STATUS   ifStatus;
		DBGPRINT(DBG_ISR|DBG_HELP, (L"=> Calling If_GetUpldDataTypeAndLength(%x)\r\n", ucLHostIntStatus));
		ifStatus = If_GetUpldDataTypeAndLength(pIntParam->pAdapter, &ucCardStatus, &macEvent, &usLength, &p_pkt, &ucLHostIntStatus);
		if ( !IF_IS_SUCCESS(ifStatus) )
		{  
			DBGPRINT(DBG_ERROR,(L"Read Data lenth/type/blcok fail....\r\n"));
			break; 
		}
		DBGPRINT(DBG_ISR|DBG_HELP, (L"=> Handling Interrupt: %s\r\n", GetIntrTypeStrn(ucCardStatus)));
		switch ( ucCardStatus ) 
        {
            case IF_DATA_PKT:
                pRxBufVM = (PVOID)p_pkt;
                if( pRxBufVM != NULL)
                {  
                    if((*((UCHAR *)((ULONG)pRxBufVM + 14)) == 0xaa ) &&  
                              (*((UCHAR *)((ULONG)pRxBufVM + 15)) == 0xaa ) &&
                              (*((UCHAR *)((ULONG)pRxBufVM + 16)) == 0x03 ) &&
                              (*((UCHAR *)((ULONG)pRxBufVM + 20)) == 0x88 ) )
                    {
                        if ( (*((UCHAR *)((ULONG)pRxBufVM + 21)) == 0x8e ) ||
                                  (*((UCHAR *)((ULONG)pRxBufVM + 21)) == 0xc7 ) )   // fix pre-auth
                        {
                            usLength -= 8;
                            NdisMoveMemory((PUCHAR)((ULONG)pRxBufVM + 12),(PUCHAR)((ULONG)pRxBufVM + 20), (usLength-20));   
                        }
					}
                }
                HandleRxReadyEvent(pIntParam->pAdapter->pRxParam);

                if (pRxBufVM != NULL && p_pkt->Len>=14 ) 
				{
                    UCHAR   *bufpt = ((UCHAR*)pRxBufVM)+14;
                    wlan_ccx_RxPktFilter(pIntParam->pAdapter, bufpt);
                }
                break;
                                       
            case IF_CMD_PKT:
                ASSERT(pIntParam->pAdapter->CurCmd != NULL);
                ASSERT(pIntParam->pAdapter->bPSConfirm!=TRUE);
                if(pIntParam->pAdapter->bPSConfirm==FALSE)
                {
                    if(pIntParam->pAdapter->CurCmd != NULL)
                    {
                        // still making 1 buffer copy, need to improve on this
                        NdisMoveMemory(pIntParam->pAdapter->CurCmd->BufVirtualAddr, p_pkt->Code, usLength);
                    }  
                    HandleCommandFinishedEvent(pIntParam->pAdapter); 
                }
                break;
                  
            case IF_MAC_EVENT:
                RETAILMSG(0, (L"=>MacEvent: %xh, psconfirm: %d\r\n", macEvent, pIntParam->pAdapter->bPSConfirm));
                if (pIntParam->pAdapter->bPSConfirm == FALSE) {
                    HandleMACEvent(pIntParam->pAdapter, macEvent);
                } else if(macEvent==MACREG_INT_CODE_PS_AWAKE || macEvent==MACREG_INT_CODE_HOST_SLEEP_AWAKE)
                {
                    pIntParam->pAdapter->bPSConfirm=FALSE; 
                    HandleMACEvent(pIntParam->pAdapter,macEvent);
                }
                else if (macEvent==MACREG_INT_CODE_BG_SCAN_REPORT)  //For under PS
                {
                    HandleMACEvent(pIntParam->pAdapter,macEvent);
                }
                if(pIntParam->pAdapter->plusDebug)
                {
                    DBGPRINT(DBG_V9, (L"IF_MAC_DONE \n"));
                } 
                break;
            default:
                // SHOULD not be here
                DBGPRINT(DBG_ERROR, (L"MrvDrvSdioIntTimer: ERROR, received unexpected type from FW: %d\n", p_pkt->Type));
                ASSERT(FALSE);
                break;
        }
    }
    return;
}


///////////////////////////////////////////////////////////////////////////////
//  MrvIstThread -  IST to get back to miniport context during
//                      interrupt
//  Input:  
//  Output: 
//  Returns:  
//  Notes:  
///////////////////////////////////////////////////////////////////////////////
void MrvIstThread(PVOID pContext)
{
	PINT_PARAM		pIntParam = (PINT_PARAM)pContext;
    BOOLEAN                     ErrorFlag=FALSE;
	UCHAR usWrongPktLen=0;    
	IF_REGVAL	ucLHostIntStatus=0;

	while(1) 
	{
		if (If_IsMoreIntr(pIntParam->pAdapter) == FALSE) {
			break;
		}
		if ((pIntParam->pAdapter->bPSConfirm==TRUE) && 
			(If_UpLoadReady(pIntParam->ucGHostIntStatus) == FALSE)) {
			///While the sleep_confirm has sent, only accept "Upload_event", 
			break;
		}

		EnterCriticalSection(&pIntParam->IntCriticalSection);   
		ucLHostIntStatus = pIntParam->ucGHostIntStatus;
		pIntParam->ucGHostIntStatus &= ~ucLHostIntStatus;
		LeaveCriticalSection(&pIntParam->IntCriticalSection);   

		///
		///If there are any software interrupt
		///
		if (pIntParam->SoftIntStatus) {
			HandleSoftInterrupt(pIntParam);
		}
		//DBGPRINT(DBG_ISR|DBG_HELP, (L"MrvIstThread: waken(%xh), %d\r\n", ucLHostIntStatus, GetPendingTxPktCount(pIntParam->pAdapter->pTxParam)));
		if ( pIntParam->pAdapter->SurpriseRemoved == TRUE )
		{
			DBGPRINT(DBG_ISR,(L"%s: Adapter already removed!\n", TEXT(__FUNCTION__)));
			break;
		}
		if (If_UpLoadReady(ucLHostIntStatus)) {
			DBGPRINT(DBG_ISR, (L"==> This is upload interrupt (%x)\r\n", ucLHostIntStatus));
		 	HandleUpldInterrupt(pIntParam, IF_UpLoadIntrMask(ucLHostIntStatus));
		}
		/////////////////////////////////////////////////////
		//HostIntStatus & 0x2 
		if (If_DnLoadReady(ucLHostIntStatus))
		{
			///ASSERT(IsTxPktSending(pIntParam->pAdapter->pTxParam)==TRUE);
			HandleTxSingleDoneEvent(pIntParam->pAdapter);
		}
	} // end of while loop  

	return;
}

NDIS_STATUS SetupIntIst( IN PVOID pIntP )
{
    PINT_PARAM		pIntParam = (PINT_PARAM) pIntP;
    
    if ( pIntParam->pAdapter->dwMrvlSdioInt == USE_MRVL_SDIO_INT )
    {
        DBGPRINT( DBG_LOAD, ( L"Setup IST for MRVL SDIO interrupt\r\n" ) );
        pIntParam->hControllerInterruptEvent = wlan_create_thread(MrvDirectIstThread, pIntParam, pIntParam->pAdapter->IstThreadPriority);
    }
    else
    {
        DBGPRINT( DBG_LOAD, ( L"Setup IST for normal SDIO interrupt\r\n" ) );
        pIntParam->hControllerInterruptEvent = wlan_create_thread(MrvIstThread, pIntParam, pIntParam->pAdapter->IstThreadPriority);
    }

    if (pIntParam->hControllerInterruptEvent == NULL) {
        return NDIS_STATUS_FAILURE;
    }

    return NDIS_STATUS_SUCCESS;
}

/* ========================================================================= */
NDIS_STATUS InitIntParam(IN PPVOID ipIntParam, IN PINTINIT pIntInit)
{
    NDIS_STATUS 	result;
    PINT_PARAM		pIntParam;

    pIntParam = *ipIntParam = (PVOID)&IntParam;
    NdisZeroMemory(pIntParam, sizeof(INT_PARAM));
	
    pIntParam->pAdapter = pIntInit->pAdapter;
    InitializeCriticalSection(&pIntParam->IntCriticalSection);
    result = NDIS_STATUS_SUCCESS;
	return result;
}

NDIS_STATUS	DeinitIntParam(IN PVOID ipIntParam)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    NDIS_STATUS result = NDIS_STATUS_SUCCESS;

    DeleteCriticalSection(&pIntParam->IntCriticalSection);
    wlan_destroy_thread(pIntParam->hControllerInterruptEvent);
	
    return result;
}

HANDLE	GetIstWaitEvent(IN PVOID ipIntParam)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    return pIntParam->hControllerInterruptEvent;
}

ULONG	GetSoftIntr(IN PVOID ipIntParam)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    return pIntParam->SoftIntStatus;
}

VOID	SetSoftIntr(IN PVOID ipIntParam, IN ULONG SoftIntr)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    pIntParam->SoftIntStatus |= SoftIntr;
    return;
}

IF_REGVAL	GetHostIntr(IN PVOID ipIntParam)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    return pIntParam->ucGHostIntStatus;
}

VOID	SetHostIntr(IN PVOID ipIntParam, IN IF_REGVAL HostIntr)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    pIntParam->ucGHostIntStatus |= HostIntr;
    return;
}

ULONG	GetWaitIntType(IN PVOID ipIntParam)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    return pIntParam->WaitEventType;
}

VOID	ClearWaitIntType(IN PVOID ipIntParam, IN ULONG ClrWaitIntType)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    pIntParam->WaitEventType &= ~ClrWaitIntType;
    return;
}

CRITICAL_SECTION* GetIntrCS(IN PVOID ipIntParam)
{
    PINT_PARAM pIntParam = (PINT_PARAM)ipIntParam;
    return &pIntParam->IntCriticalSection;
}


        typedef VOID (*BT_INT_FUNC) ( IN PVOID, IN UCHAR );
        static PVOID                  s_pBtContext = NULL; // it points to an Adapter of BT
        static BT_INT_FUNC       s_pBtIntHandler = NULL;

inline void SdioWriteBTInterruptStatus( PMRVDRV_ADAPTER pAdapter, UCHAR ucIntStatus )
{
    SD_API_STATUS    status;

    status = If_WriteRegister(pAdapter,
                                        2,
                                        HCR_HOST_INT_STATUS_REGISTER, 
                                        FALSE,
                                        &ucIntStatus,
                                        sizeof(ucIntStatus));
    
    if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ISR,(L"IST is failed to write host interrupt status register (0x%x)\r\n", status));
    }
}

inline UCHAR SdioReadBTInterruptStatus( PMRVDRV_ADAPTER pAdapter )
{
    SD_API_STATUS    status;
    UCHAR                  ucHostIntStatus = 0;

    status = If_ReadRegister(pAdapter,
                                        2,
                                        HCR_HOST_INT_STATUS_REGISTER, 
                                        FALSE,
                                        &ucHostIntStatus,
                                        sizeof(ucHostIntStatus));
    
    if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ISR,(L"IST is failed to read host interrupt status register (0x%x)\r\n", status));
        return 0;
    }

    return ucHostIntStatus;
}

VOID MrvSetBtContext( PVOID pBtContext, PVOID pBtIntHandler )
{
    s_pBtContext = pBtContext;
    s_pBtIntHandler = (BT_INT_FUNC) pBtIntHandler;
    
    NKDbgPrintfW( L"[int] set BT context (0x%x) and int handler (0x%x)\r\n", s_pBtContext, s_pBtIntHandler );
}

inline UCHAR SdioReadInterruptPending( PMRVDRV_ADAPTER pAdapter )
{
    SD_API_STATUS    status;
    UCHAR            ucIntPending = 0;

    status = If_ReadRegister(pAdapter,
                                        0,
                                        SD_IO_REG_INT_PENDING, 
                                        FALSE,
                                        &ucIntPending,
                                        sizeof(ucIntPending));
    
    if (!SD_API_SUCCESS(status))
    {
        DBGPRINT(DBG_ISR,(L"IST is failed to read host interrupt pending register (0x%x)\r\n", status));
        return 0;
    }

    return ucIntPending;
}


static VOID HandleHostInterrupt(PINT_PARAM	pIntParam)
{
    IF_REGVAL            HostIntStatus = 0;
    SD_API_STATUS    sdStatus;

    UCHAR                   ucIntPending;
    UCHAR                   ucBtIntStatus = 0;

    ucIntPending = SdioReadInterruptPending( pIntParam->pAdapter );
    if ( ucIntPending & 0x9 )
    {
        NKDbgPrintfW( L"[??] abnormal pend! 0x%x\r\n", ucIntPending  );
    }
    
    if ( ucIntPending & 0x4 )
    {
        ucBtIntStatus = SdioReadBTInterruptStatus( pIntParam->pAdapter );
        if ( s_pBtIntHandler )
            s_pBtIntHandler( s_pBtContext, ucBtIntStatus );
    }

    if ( !(ucIntPending & 0x2) )
        goto _end_of_int_proc;

    sdStatus = If_FetchHostIntr( pIntParam->pAdapter, &HostIntStatus );

    // ++ only for debugging
    if ( HostIntStatus == 0 )
    {
        static DWORD    s_nIntCnt=0;
        NKDbgPrintfW( L"[tt] ??? host int is fired, but no int status (cnt=%d, status=0x%x)\r\n", ++s_nIntCnt, sdStatus );
    }
    // --

    while ( HostIntStatus )
    {
        // ++ only for debugging
        if ( HostIntStatus > 3 )
            NKDbgPrintfW( L"[tt] ??? invalid int status = 0x%x\r\n", HostIntStatus );
        // --
        
        if ( If_UpLoadReady( HostIntStatus ) ) 
        {
            DBGPRINT(DBG_ISR, (L"==> This is upload interrupt (%x)\r\n", HostIntStatus));
            HandleUpldInterrupt( pIntParam, IF_UpLoadIntrMask( HostIntStatus ) );
        }

        if ( If_DnLoadReady( HostIntStatus ) )
        {
            ///ASSERT(IsTxPktSending(pIntParam->pAdapter->pTxParam)==TRUE);
            HandleTxSingleDoneEvent(pIntParam->pAdapter);
        }

        HostIntStatus = 0;
        sdStatus = If_FetchHostIntr( pIntParam->pAdapter, &HostIntStatus );
    }

    _end_of_int_proc:
#ifdef _MRVL_SDIO_INT
    sdStatus = SDSetCardFeature(pIntParam->pAdapter->hDevice,
                                0xFF107502,
                                NULL,
                                0);
#endif ///_MRVL_SDIO_INT
    // NKDbgPrintfW( L"$ clr int (0x%x)\r\n", sdStatus );

    if ( ucIntPending & 0x4 )
    {
        SdioWriteBTInterruptStatus( pIntParam->pAdapter, 0x1c );
    }

    if ( ucIntPending & 0x2 )

    If_ResetIntr( pIntParam->pAdapter, 0x3 );

}

void MrvDirectIstThread(PVOID pContext)
{
    PINT_PARAM		pIntParam = (PINT_PARAM)pContext;
    BOOLEAN                     ErrorFlag=FALSE;
    UCHAR usWrongPktLen=0;    
    IF_REGVAL	ucLHostIntStatus=0;
  
    while(  pIntParam->SoftIntStatus || pIntParam->ucGHostIntStatus )
    {
     
        if ( pIntParam->pAdapter->SurpriseRemoved == TRUE )
        {
            DBGPRINT(DBG_ISR,(L"%s: Adapter already removed!\n", TEXT(__FUNCTION__)));
            return;
        }

        ResetEvent(pIntParam->hControllerInterruptEvent);

        if ( pIntParam->SoftIntStatus )
        {
            HandleSoftInterrupt( pIntParam );
        }

        if ( pIntParam->ucGHostIntStatus )
        {
            pIntParam->ucGHostIntStatus = 0;
            HandleHostInterrupt( pIntParam );
        }
    }

    return;
}

VOID SdioMrvlIntCallback( PVOID pContext )
{
    PINT_PARAM pIntParam = (PINT_PARAM)pContext;

    pIntParam->pAdapter->IntWARTimeoutCount = 0;
    
    pIntParam->ucGHostIntStatus |= 0x8;
    SetEvent( pIntParam->hControllerInterruptEvent );
}


